package com.recipedb.recipedb.service.impl;

import com.recipedb.recipedb.entity.Recipe;
import com.recipedb.recipedb.repository.FavoriteRepository;
import com.recipedb.recipedb.repository.RecipeRepository;
import com.recipedb.recipedb.service.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RecipeServiceImpl implements RecipeService {

    @Autowired
    private RecipeRepository repository;

    @Autowired
    private FavoriteRepository favoriteRepository;

    @Override
    public Recipe save(Recipe entity) {
        Recipe savedRecipe = repository.save(entity);
        return setFavoriteCountForRecipe(savedRecipe);
    }

    @Override
    public void deleteById(Long id) {
        repository.deleteById(id);
    }

    @Override
    public Optional<Recipe> findById(Long id) {
        Optional<Recipe> recipe = repository.findById(id);
        recipe.ifPresent(this::setFavoriteCountForRecipe);
        return recipe;
    }

    @Override
    public List<Recipe> findAll() {
        List<Recipe> recipes = repository.findAll();
        return setFavoriteCountsForRecipes(recipes);
    }

    @Override
    public List<Recipe> findByUserId(Long userId) {
        List<Recipe> recipes = repository.findByUserIdOrderByCreatedAtDesc(userId);
        return setFavoriteCountsForRecipes(recipes);
    }

    @Override
    public List<Recipe> search(String keyword, String tag, String category, String ingredient) {
        List<Recipe> recipes;
        if (keyword != null && !keyword.isEmpty()) {
            recipes = repository.searchByTitle(keyword);
        } else if (tag != null && !tag.isEmpty()) {
            recipes = repository.searchByTag(tag);
        } else if (category != null && !category.isEmpty()) {
            recipes = repository.searchByCategory(category);
        } else if (ingredient != null && !ingredient.isEmpty()) {
            recipes = repository.searchByIngredient(ingredient);
        } else {
            recipes = new ArrayList<>();
        }
        return setFavoriteCountsForRecipes(recipes);
    }

    @Override
    @Transactional
    public Optional<Recipe> partialUpdate(Long id, Recipe entity) {
        return repository.findById(id)
                .map(existingRecipe -> {

                    if (entity.getTitle() != null) {
                        existingRecipe.setTitle(entity.getTitle());
                    }
                    if (entity.getDuration() != null) {
                        existingRecipe.setDuration(entity.getDuration());
                    }
                    if (entity.getDescription() != null) {
                        existingRecipe.setDescription(entity.getDescription());
                    }
                    if (entity.getCoverImage() != null) {
                        existingRecipe.setCoverImage(entity.getCoverImage());
                    }
                    if (entity.getDifficulty() != null) {
                        existingRecipe.setDifficulty(entity.getDifficulty());
                    }
                    if (entity.getDifficultyAvg() != null) {
                        existingRecipe.setDifficultyAvg(entity.getDifficultyAvg());
                    }
                    if (entity.getServings() != null) {
                        existingRecipe.setServings(entity.getServings());
                    }
                    if (entity.getUserId() != null) {
                        existingRecipe.setUserId(entity.getUserId());
                    }

                    existingRecipe.setUpdatedAt(LocalDateTime.now());

                    Recipe updatedRecipe = repository.save(existingRecipe);
                    return setFavoriteCountForRecipe(updatedRecipe);
                });
    }
    @Override
    public Long getFavoriteCountByRecipeId(Long recipeId) {
        return favoriteRepository.countByRecipeId(recipeId);
    }

    @Override
    public Long getTotalFavoritesByUserId(Long userId) {
        return favoriteRepository.countTotalFavoritesByUserId(userId);
    }

    @Override
    public List<Recipe> setFavoriteCountsForRecipes(List<Recipe> recipes) {
        if (recipes == null || recipes.isEmpty()) {
            return recipes;
        }
        List<Long> recipeIds = recipes.stream()
                .map(Recipe::getId)
                .collect(Collectors.toList());

        List<Object[]> results = favoriteRepository.countByRecipeIds(recipeIds);
        Map<Long, Long> favoriteCounts = new HashMap<>();

        for (Object[] result : results) {
            Long recipeId = (Long) result[0];
            Long count = ((Number) result[1]).longValue();
            favoriteCounts.put(recipeId, count);
        }
        recipes.forEach(recipe -> {
            Long count = favoriteCounts.getOrDefault(recipe.getId(), 0L);
            recipe.setFavoriteCount(count);
        });

        return recipes;
    }

    @Override
    public Recipe setFavoriteCountForRecipe(Recipe recipe) {
        if (recipe != null) {
            Long count = getFavoriteCountByRecipeId(recipe.getId());
            recipe.setFavoriteCount(count);
        }
        return recipe;
    }

    @Override
    @Transactional
    public Map<Long, Long> refreshAndGetChangedFavoriteCounts() {
        List<Recipe> allRecipes = repository.findAll();
        if (allRecipes.isEmpty()) {
            return new HashMap<>();
        }

        List<Long> recipeIds = allRecipes.stream()
                .map(Recipe::getId)
                .collect(Collectors.toList());

        List<Object[]> currentCounts = favoriteRepository.countByRecipeIds(recipeIds);
        Map<Long, Long> currentFavoriteCounts = new HashMap<>();

        for (Object[] result : currentCounts) {
            Long recipeId = (Long) result[0];
            Long count = ((Number) result[1]).longValue();
            currentFavoriteCounts.put(recipeId, count);
        }
        for (Long recipeId : recipeIds) {
            currentFavoriteCounts.putIfAbsent(recipeId, 0L);
        }
        Map<Long, Long> changedCounts = new HashMap<>();
        allRecipes.forEach(recipe -> {
            Long currentCount = currentFavoriteCounts.get(recipe.getId());
            Long previousCount = recipe.getFavoriteCount() != null ? recipe.getFavoriteCount() : 0L;
            if (!currentCount.equals(previousCount)) {
                changedCounts.put(recipe.getId(), currentCount);
                recipe.setFavoriteCount(currentCount);
            }
        });
        if (!changedCounts.isEmpty()) {
            List<Recipe> changedRecipes = allRecipes.stream()
                    .filter(recipe -> changedCounts.containsKey(recipe.getId()))
                    .collect(Collectors.toList());
            repository.saveAll(changedRecipes);
        }

        return changedCounts;
    }

    // ========== 实现新增的“我的发布”分页搜索 ==========
    @Override
    public Page<Recipe> searchUserRecipes(
            Long userId,
            String keyword,
            String tag,
            String category,
            String ingredient,
            Integer pageNum,
            Integer pageSize) {

        // 1. 参数兜底（和草稿/收藏接口对齐）
        int page = pageNum == null || pageNum < 1 ? 0 : pageNum - 1;
        int size = pageSize == null || pageSize < 1 ? 10 : pageSize;

        // 2. 步骤1：查询当前用户发布的所有食谱（已按创建时间倒序）
        List<Recipe> allUserRecipes = repository.findByUserIdOrderByCreatedAtDesc(userId);
        if (allUserRecipes.isEmpty()) {
            return new PageImpl<>(new ArrayList<>(), PageRequest.of(page, size), 0);
        }

        // 3. 步骤2：复用原有search方法，筛选符合条件的所有食谱（全局）
        List<Recipe> globalFilteredRecipes = search(keyword, tag, category, ingredient);
        if (globalFilteredRecipes.isEmpty()) {
            return new PageImpl<>(new ArrayList<>(), PageRequest.of(page, size), 0);
        }

        // 4. 步骤3：只保留当前用户发布的食谱（交集）
        Set<Long> userRecipeIds = allUserRecipes.stream().map(Recipe::getId).collect(Collectors.toSet());
        List<Recipe> userFilteredRecipes = globalFilteredRecipes.stream()
                .filter(recipe -> userRecipeIds.contains(recipe.getId()))
                .collect(Collectors.toList());

        // 5. 步骤4：手动分页（复用原有收藏/草稿逻辑）
        int startIndex = page * size;
        int endIndex = Math.min(startIndex + size, userFilteredRecipes.size());
        List<Recipe> pagedRecipes = startIndex >= userFilteredRecipes.size()
                ? new ArrayList<>()
                : userFilteredRecipes.subList(startIndex, endIndex);

        // 6. 步骤5：填充收藏数（复用原有逻辑）
        setFavoriteCountsForRecipes(pagedRecipes);

        // 7. 返回分页结果
        return new PageImpl<>(
                pagedRecipes,
                PageRequest.of(page, size),
                userFilteredRecipes.size()
        );
    }

}